﻿using System;
using System.Linq;
using System.Windows;
using XmlStudio.ViewModel.Node;
using XmlStudio.DataLayer;
using System.Collections.Generic;
using XmlStudio.Interfaces;
using System.Windows.Data;
using System.Windows.Controls;
using System.Text;

namespace XmlStudio.Dialogs {
    /// <summary>
    /// Interaction logic for AddXsdAttributeDialog.xaml
    /// </summary>
    public partial class AddXsdAttributeDialog : Window {
        #region Properties
        /// <summary>
        /// Gets the <see cref="XmlAttributeViewModel"/> as a new attribute.
        /// </summary>
        public XsdAttributeViewModel Attribute {
            get;
            set;
        }

        public IEnumerable<IBigXmlAttribute> OptionalAttributes {
            get { return XsdAttributeFactory.ListOptionalAttributes(this.ElementType).Union(XsdAttributeFactory.ListRequiredAttributes(this.ElementType)); }
        }

        /// <summary>
        /// Gets or sets the type of element being edited.
        /// </summary>
        public XsdElement ElementType { get; set; }
        #endregion

        #region Fields
        IXsdModel model;
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="AddAttributeDialog"/> class.
        /// </summary>
        public AddXsdAttributeDialog(XsdElement elementType, IXsdModel model) {
            this.ElementType = elementType;
            this.model = model;
            InitializeComponent();

            this.UpdateValues(this.SelectedAttributeName, this.ElementType);
        }

        public AddXsdAttributeDialog(XsdElement elementType, XsdAttributeViewModel attribute, IXsdModel model) {
            this.ElementType = elementType;
            this.Attribute = attribute;
            this.model = model;
            InitializeComponent();

            this.SelectAttributeName(attribute.LocalName);
            this.UpdateValues(attribute.LocalName, elementType);
            this.SelectValue(attribute.Value);
        }
        #endregion

        #region Properties
        private bool IsValueComboBoxVisible {
            get {
                if(this.localValuesComboBox.Visibility == System.Windows.Visibility.Visible) {
                    return true;
                }

                return false;
            }
        }

        private bool IsValueListBoxVisible {
            get {
                if(this.localValuesListBox.Visibility == System.Windows.Visibility.Visible) {
                    return true;
                }

                return false;
            }
        }

        private bool IsValueTextBoxVisible {
            get {
                if(this.valueTextBox.Visibility == System.Windows.Visibility.Visible) {
                    return true;
                }

                return false;
            }
        }

        private string SelectedAttributeName {
            get { return this.localNameComboBox.SelectedValue.ToString(); }
        }

        private string SelectedPrefix {
            get { return this.prefixTextBox.Text; }
        }

        private string SelectedNamespaceUri {
            get { return this.namespaceURITextBox.Text; }
        }
        #endregion

        #region Event handlers
        protected override void OnInitialized(EventArgs e) {
            base.OnInitialized(e);

            this.prefixTextBox.Focus();
        }

        private void OnSubmit(object sender, RoutedEventArgs e) {
            this.Attribute = new XsdAttributeViewModel {
                LocalName = this.SelectedAttributeName,
                Prefix = this.SelectedPrefix,
                NamespaceURI = this.SelectedNamespaceUri,
                Value = this.GetValue(),
                Required = this.Attribute != null ? this.Attribute.Required : false
            };

            this.DialogResult = true;
            this.Close();
        }

        private void OnLocalNameComboBoxSelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e) {
            var attributeName = localNameComboBox.SelectedValue as string;
            UpdateValues(attributeName, this.ElementType);
        }
        #endregion

        #region Helper methods
        private void SelectAttributeName(string attributeName) {
            this.localNameComboBox.SelectedValue = attributeName;
        }

        private void SelectValue(string value) {
            if(this.IsValueComboBoxVisible) {
                this.localValuesComboBox.SelectedValue = value;
            } else if(this.IsValueListBoxVisible) {
                var values = value.Split(' ');
                foreach(var v in values) {
                    localValuesListBox.SelectedItems.Add(v);
                }
            } else {
                this.valueTextBox.Text = value;
            }
        }

        private string GetValue() {
            if(this.IsValueComboBoxVisible) {
                return localValuesComboBox.SelectedValue.ToString();
            } else if(this.IsValueListBoxVisible) {
                var sb = new StringBuilder();
                var selectedItems = localValuesListBox.SelectedItems;
                foreach(var item in selectedItems) {
                    sb.Append(" " + item.ToString());
                }

                return sb.ToString().Substring(1, sb.Length - 1);
            } else {
                return valueTextBox.Text;
            }
        }

        private void UpdateValues(string attributeName, XsdElement elementType) {
            // try to get values bindable to combobox -> these are available only for certain attributes
            var binding = BindReferencableElements(attributeName, elementType, this.model);
            if(binding.Source != null) {
                // if we have values for combobox but attribute name is memberTypes, show the listbox insted
                if(attributeName == "memberTypes" && this.ElementType == XsdElement.Union) {
                    localValuesListBox.SetBinding(ListBox.ItemsSourceProperty, binding);
                    ShowValueListBox();
                    HideValueComboBox();
                    HideValueTextBox();
                } else {
                    localValuesComboBox.SetBinding(ComboBox.ItemsSourceProperty, binding);
                    localValuesComboBox.SelectedIndex = 0;
                    ShowValueComboBox();
                    HideValueListBox();
                    HideValueTextBox();
                }
            } else {
                HideValueComboBox();
                HideValueListBox();
                ShowValueTextBox();
            }
        }

        #region Data
        public static IEnumerable<string> GetBuiltInTypes(IXsdModel model) {
            if(!string.IsNullOrEmpty(model.XsPrefix)) {
                return model.ListBuiltInDataTypes().Select(x => string.Format("{0}:{1}", model.XsPrefix, XsdDataTypeHelper.GetTypeName(x)));
            } else {
                return model.ListBuiltInDataTypes().Select(x => XsdDataTypeHelper.GetTypeName(x));
            }
        }

        public static Binding BindReferencableElements(string attributeName, XsdElement elementType, IXsdModel model) {
            var binding = new Binding();
            binding.Mode = BindingMode.OneWay;

            switch(attributeName) {
                case "ref":
                    switch(elementType) {
                        case XsdElement.Attribute: {
                                binding.Source = model.NamedAttributes;
                            }
                            break;
                        case XsdElement.AttributeGroup: {
                                binding.Source = model.NamedAttributeGroups;
                            }
                            break;
                        case XsdElement.Element: {
                                binding.Source = model.NamedElements;
                            }
                            break;
                        case XsdElement.Group: {
                                binding.Source = model.NamedGroups;
                            }
                            break;
                    }
                    break;
                case "type":
                    switch(elementType) {
                        case XsdElement.Attribute:
                            binding.Source = GetBuiltInTypes(model).Union(model.NamedSimpleTypes);
                            break;
                        case XsdElement.Element:
                            binding.Source = GetBuiltInTypes(model).Union(model.NamedSimpleTypes).Union(model.NamedComplexTypes);
                            break;
                    }
                    break;
                case "substitutionGroup":
                    switch(elementType) {
                        case XsdElement.Element:
                            binding.Source = model.NamedElements;
                            break;
                    }
                    break;
                case "base":
                    switch(elementType) {
                        case XsdElement.Extension:
                            binding.Source = GetBuiltInTypes(model).Union(model.NamedSimpleTypes).Union(model.NamedComplexTypes);
                            break;
                        case XsdElement.Restriction:
                            binding.Source = GetBuiltInTypes(model).Union(model.NamedSimpleTypes).Union(model.NamedComplexTypes);
                            break;
                    }
                    break;
                case "refer":
                    switch(elementType) {
                        case XsdElement.KeyRef:
                            binding.Source = model.NamedKeyElements.Union(model.NamedUniqueElements);
                            break;
                    }
                    break;
                case "itemType":
                    switch(elementType) {
                        case XsdElement.List:
                            binding.Source = GetBuiltInTypes(model).Union(model.NamedSimpleTypes);
                            break;
                    }
                    break;
                case "memberTypes":
                    switch(elementType) { // listbox
                        case XsdElement.Union:
                            binding.Source = GetBuiltInTypes(model).Union(model.NamedSimpleTypes);
                            break;
                    }
                    break;
            }

            return binding;
        }
        #endregion

        #region Hiding
        private void HideValueComboBox() {
            if(localValuesComboBox != null) {
                localValuesComboBox.Visibility = System.Windows.Visibility.Collapsed;
            }
        }

        private void HideValueListBox() {
            if(localValuesListBox != null) {
                localValuesListBox.Visibility = System.Windows.Visibility.Collapsed;
            }
        }

        private void HideValueTextBox() {
            if(valueTextBox != null) {
                valueTextBox.Visibility = System.Windows.Visibility.Collapsed;
            }
        }
        #endregion

        #region Showing
        private void ShowValueComboBox() {
            if(localValuesComboBox != null) {
                localValuesComboBox.Visibility = System.Windows.Visibility.Visible;
            }
        }

        private void ShowValueListBox() {
            if(localValuesListBox != null) {
                localValuesListBox.Visibility = System.Windows.Visibility.Visible;
            }
        }

        private void ShowValueTextBox() {
            if(valueTextBox != null) {
                valueTextBox.Visibility = System.Windows.Visibility.Visible;
            }
        }
        #endregion
        #endregion
    }
}
